package com.mornd.gulimall.order.config.mq;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: mornd
 * @dateTime: 2023/8/21 - 10:52
 * <p>
 * 交换机类型：
 * direct：匹配路由键一模一样的queue
 * fanout：忽略路由键，发送给所有绑定的queue
 * topic：匹配表达式
 * #匹配0个或多个单词，例如，"fruit.#" 可以匹配 "fruit.apple"、"fruit.apple.red"、"fruit"
 * *匹配一个单词，例如，"*.apple" 可以匹配"red.apple"，但不能匹配"apple"和"red.green.apple"
 * <p>
 * publisher - exchange confirmCallback确认模式
 * exchange - queue returnCallback
 * queue - consumer ack机制
 */

@Slf4j
@EnableRabbit
@Configuration
public class RabbitMQConfig {

    /**
     * mq消息转换器
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();

        template.setConfirmCallback(confirmCallback());
        template.setReturnsCallback(returnsCallback());

        configurer.configure(template, connectionFactory);
        return template;
    }

    /**
     * 消息抵达服务回调
     *
     * @return
     */
    private RabbitTemplate.ConfirmCallback confirmCallback() {
        return (correlationData, ack, cause) -> {
            /**
             * 消息发送完无论成功或失败都会回调
             * ack=true 表示消息正确到达exchange
             * cause：失败原因 channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'fanout123' in vhost '/', class-id=60, method-id=40)
             */
            log.info("confirmCallback：{},{},{}", correlationData, ack, cause);
        };
    }

    private RabbitTemplate.ReturnsCallback returnsCallback() {
        /**
         * 只有错误投递到队列才会回调
         */
        return returned -> {
            // (Body:'[B@432be482(byte[822])' MessageProperties [headers={__TypeId__=com.mornd.gulimall.order.entity.Order}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
            Message message = returned.getMessage();
            // 312
            int replyCode = returned.getReplyCode();
            // NO_ROUTE
            String replyText = returned.getReplyText();
            String exchange = returned.getExchange();
            String routingKey = returned.getRoutingKey();
            log.error("消息投递到队列失败，returnsCallback：{},{},{},{},{}", message, replyCode, replyText, exchange, routingKey);
        };
    }
}
